﻿// <copyright file="IEcoObjectExtension.cs" company="Codeplex project Eco.Contrib"> 
// The MIT License (MIT) 
// </copyright> 
// <author>Peter Buchmann</author> 

namespace Eco.Contrib
{
    using System.Linq;
    using Eco.Cache;
    using Eco.Framework;
    using Eco.ObjectRepresentation;
    using Eco.Services;
    using Eco.UmlRt;

    /// <summary>
    /// Extension methods for interface IEcoObject
    /// </summary>
    public static class IEcoObjectExtension
    {
        /// <summary>
        /// Copies all attributes from SourceObject to TargetObject that have the same name in both classes.
        /// It is not necessary that both objects have the same type.
        /// </summary>
        /// <param name="targetObject">The target object.</param>
        /// <param name="sourceObject">The source object.</param>
        public static void CopyAttributesByName(this IEcoObject targetObject, IEcoObject sourceObject)
        {
            if (targetObject != null && sourceObject != null)
            {
                IClass sourceClass = sourceObject.AsIObject().UmlClass;
                IClass targetClass = targetObject.AsIObject().UmlClass;
                foreach (IStructuralFeature feature in sourceClass.AllStructuralFeatures)
                {
                    if (feature.FeatureType == FeatureType.Attribute)
                    {
                        if (targetClass.AllStructuralFeatures.GetItemByName(feature.Name) != null)
                        {
                            targetObject.AsIObject().Properties[feature.Name].AsObject = sourceObject.AsIObject().Properties[feature.Name].AsObject;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Copies all attributes from SourceObject to TargetObject that have the same name in both classes.
        /// Associations are only copied when the name of the association end is added in the string list associationNames.
        /// It is not necessary that both objects have the same type.
        /// </summary>
        /// <param name="targetObject">The target object.</param>
        /// <param name="sourceObject">The source object.</param>
        /// <param name="associationNames">The association names.</param>
        public static void CopyAttributesByName(this IEcoObject targetObject, IEcoObject sourceObject, string[] associationNames)
        {
            targetObject.CopyAttributesByName(sourceObject);

            if (targetObject != null && sourceObject != null)
            {
                IClass sourceClass = sourceObject.AsIObject().UmlClass;
                IClass targetClass = targetObject.AsIObject().UmlClass;
                foreach (IStructuralFeature feature in sourceClass.AllStructuralFeatures)
                {
                    if (associationNames.Contains<string>(feature.Name) && (feature.FeatureType == FeatureType.AssociationEnd))
                    {
                        if (feature.Multiplicity.IsSingle)
                        {
                            if (targetClass.AllStructuralFeatures.GetItemByName(feature.Name) != null)
                            {
                                targetObject.AsIObject().Properties[feature.Name].AsObject = sourceObject.AsIObject().Properties[feature.Name].AsObject;
                            }
                        }
                        else
                        {
                            foreach (IElement element in sourceObject.AsIObject().Properties[feature.Name].GetAsCollection())
                            {
                                targetObject.AsIObject().Properties[feature.Name].GetAsCollection().Add(element);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Copies all attributes from SourceObject to TargetObject that have the same name in both classes.
        /// Associations are only copied when the name of the association end is in commaSeparatedAssociationNames.
        /// It is not necessary that both objects have the same type.
        /// </summary>
        /// <param name="targetObject">The target object.</param>
        /// <param name="sourceObject">The source object.</param>
        /// <param name="commaSeparatedAssociationNames">The comma Separated association names.</param>
        public static void CopyAttributesByName(this IEcoObject targetObject, IEcoObject sourceObject, string commaSeparatedAssociationNames)
        {
            if (targetObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            if (commaSeparatedAssociationNames == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            targetObject.CopyAttributesByName(sourceObject, commaSeparatedAssociationNames.Split(','));
        }

        /// <summary>
        /// Copies all attributes from SourceObject to TargetObject that have the same name in both classes.
        /// Associations are only copied when the name of the association end is in commaSeparatedAssociationNames.
        /// It is not necessary that both objects have the same type.
        /// </summary>
        /// <param name="targetObject">The target object.</param>
        /// <param name="sourceObject">The source object.</param>
        /// <param name="commaSeparatedAssociationNames">The comma Separated association names.</param>
        public static void CopyAttributesByName(this IEcoObject targetObject, IObject sourceObject, string commaSeparatedAssociationNames)
        {
            if (targetObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            if (commaSeparatedAssociationNames == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            targetObject.CopyAttributesByName(sourceObject, commaSeparatedAssociationNames.Split(','));
        }

        /// <summary>
        /// Deletes the Object.
        /// </summary>
        /// <param name="ecoObject">The eco object.</param>
        public static void Delete(this IEcoObject ecoObject)
        {
            if (ecoObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            ecoObject.AsIObject().Delete();
        }

        /// <summary>
        /// Save this only object via the current PersistenceService.
        /// </summary>
        /// <param name="ecoObject">The eco object.</param>
        public static void Save(this IEcoObject ecoObject)
        {
            if (ecoObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            ecoObject.AsIObject().ServiceProvider.GetEcoService<IPersistenceService>().UpdateDatabase(ecoObject);
        }

        /// <summary>
        /// Gets the fetched value of a attribute of a object by LoopbackIndex
        /// </summary>
        /// <param name="ecoObject">The eco object.</param>
        /// <param name="loopbackIndex">Index of the loopback.</param>
        /// <returns>Fetched value of the given attribute</returns>
        public static object FetchedValue(this IEcoObject ecoObject, int loopbackIndex)
        {
            if (ecoObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            IObject objekt = ecoObject.AsIObject();
            ICache cache = objekt.ServiceProvider.GetEcoService<Eco.Cache.ICache>();
            IStructuralFeature feature = objekt.UmlClass.AllStructuralFeatures[loopbackIndex];
            IObjectRepresentationProvider objectRepresentationProvider = objekt.ServiceProvider.GetEcoService<IObjectRepresentationProvider>();
            Locator locator = objectRepresentationProvider.LocatorForIObject(objekt as IObjectInstance);

            return cache.GetFetchedValue(locator, feature);
        }

        /// <summary>
        /// Gets the fetched value of a attribute of a object
        /// </summary>
        /// <param name="ecoObject">The eco object.</param>
        /// <param name="attribute">The attribute.</param>
        /// <returns>Fetched value of the given attribute</returns>
        public static object FetchedValue(this IEcoObject ecoObject, string attribute)
        {
            if (ecoObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            IObject tempObject = ecoObject.AsIObject();
            ICache cache = tempObject.ServiceProvider.GetEcoService<Eco.Cache.ICache>();
            IStructuralFeature feature = tempObject.UmlClass.AllStructuralFeatures.GetItemByName(attribute);
            IObjectRepresentationProvider orp = tempObject.ServiceProvider.GetEcoService<IObjectRepresentationProvider>();
            Locator locator = orp.LocatorForIObject(tempObject as IObjectInstance);

            return cache.GetFetchedValue(locator, feature);
        }

        /// <summary>
        /// True, if relevant Attribute was changed since it was fetched.
        /// </summary>
        /// <param name="ecoObject">The eco object.</param>
        /// <param name="loopbackIndex">Index of the loopback.</param>
        /// <returns><c>true</c> if value of attribute has changed since it was fetched, <c>false</c> otherwise</returns>
        public static bool AttributeValueChanged(this IEcoObject ecoObject, int loopbackIndex)
        {
            if (ecoObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            return ecoObject.FetchedValue(loopbackIndex) != ecoObject.AsIObject().Properties[loopbackIndex].AsObject;
        }

        /// <summary>
        /// True, if relevant Attribute was changed since it was fetched.
        /// </summary>
        /// <param name="ecoObject">The eco object.</param>
        /// <param name="attribute">The attribute.</param>
        /// <returns><c>true</c> if value of attribute has changed since it was fetched, <c>false</c> otherwise</returns>
        public static bool AttributeValueChanged(this IEcoObject ecoObject, string attribute)
        {
            if (ecoObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            return ecoObject.FetchedValue(attribute) != ecoObject.AsIObject().Properties[attribute].AsObject;
        }

        /// <summary>
        /// True, if any attribute of the object was changed, this means the object has to be saved.
        /// </summary>
        /// <param name="ecoObject">The eco object.</param>
        /// <returns>
        ///   <c>true</c> if the specified eco object is dirty; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsDirty(this IEcoObject ecoObject)
        {
            if (ecoObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            return ecoObject.AsIObject().ServiceProvider.GetEcoService<IStateService>().IsDirty(ecoObject);
        }

        /// <summary>
        /// EnsureRelatedObjects for a single object
        /// </summary>
        /// <param name="ecoObject">The eco object.</param>
        /// <param name="memberName">Name of the member.</param>
        /// <returns>The related IObjectList</returns>
        public static IObjectList EnsureRelatedObjects(this IEcoObject ecoObject, string memberName)
        {
            if (ecoObject == null)
            {
                throw new EcoContribException("Parameter is null");
            }

            return ecoObject.AsIObject().GetAsList().EnsureRelatedObjects(memberName);
        }
    }
}
